/**
 * 
 */
package com.tvshows.interactions;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLStreamException;

import org.apache.log4j.Logger;
import org.eclipse.swt.widgets.Display;
import org.xml.sax.SAXException;

import com.tvshows.front.controllers.favorite.FavoriteController;
import com.tvshows.front.listener.command.RefreshJob;
import com.tvshows.front.ui.recap.TableCompositeRecapUI;
import com.tvshows.front.ui.refresh.RefreshUI;
import com.tvshows.structure.bdd.BddQueries;
import com.tvshows.structure.beans.Favorite;
import com.tvshows.structure.reader.FileOperations;
import com.tvshows.structure.reader.RssReader;
import com.tvshows.structure.reader.exception.TimeOutAtteintException;

/**
 * @author tMidy
 * <p>
 * Class used to Refresh the favorites list
 * </p>
 */
public final class ObserverBdd
{

    /**
     * the shows list
     */
    private FavoriteController listFavorite;

    /**
     * The resume table
     */
    private TableCompositeRecapUI tableRecap;

    /**
     * The singleton observer
     */
    private static volatile ObserverBdd observerBdd;

    /**
     * The logger
     */
    private static final Logger LOGGER = Logger.getLogger( ObserverBdd.class );

    /**
     * The refresh view
     */
    private RefreshUI refreshUI;

    /**
     * The properties
     */
    private List<String> currentProperties;

    /**
     * Compute divider
     */
    private static final Integer DATA_HUNDRED = 100;

    /**
     * Episode number
     */
    private static final int TEN = 10;

    /**
     * the size of the list
     */
    private Integer listSize = null;

    /**
     * Constructeur
     */
    private ObserverBdd()
    {

    }

    /**
     * Refresh the favorite list
     */
    public void reloadListFavorite()
    {
        listFavorite.loadFavorites();
        listFavorite.activeDeleteOnFavorite();
    }

    /**
     * Loads the Favorite shows
     * 
     * @param refreshJob the refreshJob
     * @throws TimeOutAtteintException
     * @throws ParserConfigurationException
     * @throws SAXException
     * @throws XMLStreamException
     * @throws InterruptedException
     * @throws Exception
     */
    public void loadFavoriteDownload( RefreshJob refreshJob )
        throws IOException, XMLStreamException, SAXException, ParserConfigurationException, TimeOutAtteintException,
        InterruptedException
    {
        LOGGER.info( " Récupération de la table des favoris..." );
        final List<Favorite> favorites = BddQueries.getBddQueries().loadFavoritesBySchedule();

        if ( !favorites.isEmpty() )
        {
            LOGGER.info( " Insertion des torrents en download" );
            executeReading( favorites, refreshJob );
        }

        LOGGER.info( "Insertion des torrents en download OK!" );
    }

    /**
     * One Thread by show
     * 
     * @param favorites show list
     * @param refreshJob
     * @throws InterruptedException
     */
    private void executeReading( List<Favorite> favorites, RefreshJob refreshJob )
        throws InterruptedException
    {
        listSize = favorites.size();

        ExecutorService executorService = Executors.newFixedThreadPool( listSize );

        LOGGER.info( listSize + " séries à récupérer" );

        for ( Favorite f : favorites )
        {
            if ( !BddQueries.getBddQueries().hasHistorique( f ) )
            {
                CallableRead readThread = new CallableRead( f, refreshJob );
                executorService.submit( readThread );
            }
            Display.getDefault().asyncExec( new Runnable()
            {
                @Override
                public void run()
                {
                    try
                    {
                        updateProgress();
                    }
                    catch ( Exception e )
                    {
                        LOGGER.error( e );
                    }
                }
            } );

        }

        executorService.awaitTermination( listSize * 3, TimeUnit.SECONDS );
        executorService.shutdown();
    }

    /**
     * updates the progress bar
     */
    public void updateProgress()
    {
        int progress = DATA_HUNDRED / listSize;
        if ( !refreshUI.isSelection() )
        {
            refreshUI.setSelection( progress * listSize );
        }
        refreshUI.updateProgress( progress );
        refreshUI.updateProgressLabel( (double) DATA_HUNDRED / listSize );
    }

    /**
     * Add to table
     * 
     * @throws Exception
     */
    public void addToTable( Favorite favorite )
    {
        tableRecap.addToTable( favorite.getNameFavorite() + " Saison " + favorite.getSeasonNumber() + " Episode "
            + favorite.getLastEpisodeNumber() );
    }

    /**
     * Activates or not the progress bar
     * 
     * @param activate isActive?
     */
    public void activate( boolean activate )
    {
        refreshUI.activateProgressBar( activate );
    }

    /**
     * @param episode the episode
     * @return the formatted value of the new episode to load
     */
    private String formatEpisode( String episode )
    {
        int formatted = Integer.parseInt( episode ) + 1;
        if ( formatted < TEN )
        {
            return "0" + formatted;
        }
        return String.valueOf( Integer.parseInt( episode ) + 1 );
    }

    /**
     * Launch refresh process
     * 
     * @param urlToAdd the link to add
     * @throws IOException
     */
    private void launchProcess( String urlToAdd )
        throws IOException
    {
        if ( currentProperties == null || FileOperations.getInstance().isPropertiesHasChanged() )
        {
            currentProperties = FileOperations.getInstance().readProperties();
        }
        if ( !currentProperties.isEmpty() )
        {
            Runtime runtime = Runtime.getRuntime();
            String softwarePath = currentProperties.get( 0 );
            runtime.exec( softwarePath + " " + urlToAdd.replace( " ", "." ) );
        }
    }

    /**
     * @return the listFavorite
     */
    public FavoriteController getListFavorite()
    {
        return listFavorite;
    }

    /**
     * @param listFavorite the listFavorite to set
     */
    public void setListFavorite( FavoriteController listFavorite )
    {
        this.listFavorite = listFavorite;
    }

    /**
     * @return the tableRecap
     */
    public TableCompositeRecapUI getTableRecap()
    {
        return tableRecap;
    }

    /**
     * @param tableRecap the tableRecap to set
     */
    public void setTableRecap( TableCompositeRecapUI tableRecap )
    {
        this.tableRecap = tableRecap;
    }

    /**
     * @return the observerBdd
     */
    public static ObserverBdd getObserverBdd()
    {
        if ( observerBdd == null )
        {
            synchronized ( ObserverBdd.class )
            {
                if ( observerBdd == null )
                {
                    observerBdd = new ObserverBdd();
                }
            }
        }
        return observerBdd;
    }

    /**
     * Activates the ability to delete
     */
    public void activeDeleteOnFavorite()
    {
        listFavorite.activeDeleteOnFavorite();
    }

    /**
     * @return the selected favorite
     */
    public String getSelectedFavorite()
    {
        return listFavorite.getSelectedFavorite();
    }

    /**
     * @param refreshView the refresh view
     */
    public void setRefreshView( RefreshUI refreshView )
    {
        this.refreshUI = refreshView;
    }

    /**
     * returns the refreshUI
     */
    public RefreshUI getRefreshUI()
    {
        return this.refreshUI;

    }

    /**
     * @author tMidy
     * <p>
     * Class callable to readRss streams for the passed show
     * </p>
     */
    private class CallableRead
        implements Callable<Boolean>
    {

        /**
         * The rss reader
         */
        private RssReader reader;

        /**
         * the target .torrent url
         */
        private String target = null;

        /**
         * The show to test
         */
        private Favorite favorite;

        /**
         * The eclipse job that refreshes the table
         */
        private RefreshJob refreshJob;

        /**
         * Constructor
         * 
         * @param favorite the show to test
         * @param refreshJob the job that refreshes the table
         */
        public CallableRead( Favorite favorite, RefreshJob refreshJob )
        {
            this.favorite = favorite;
            this.refreshJob = refreshJob;
        }

        /**
         * @see java.util.concurrent.Callable#call()
         * @return true if the run is ok
         */
        @Override
        public Boolean call()
            throws Exception
        {
            LOGGER.debug( "Run Thread " + Thread.currentThread() );

            reader = new RssReader();
            target = reader.readRss( favorite.getNameFavorite().replace( " ", "+" ) );

            if ( target != null )
            {
                LOGGER.info( " Torrent trouvé pour " + favorite.getNameFavorite() );

                launchProcess( target );
                refreshJob.addToTable( favorite );
                String formattedEpisode = formatEpisode( favorite.getLastEpisodeNumber() );
                BddQueries.getBddQueries().updateFavorite( favorite.getNameFavorite(), favorite.getSeasonNumber(),
                    formattedEpisode, favorite.getScheduleFavorite() );

            }
            BddQueries.getBddQueries().addHistorique( favorite );
            LOGGER.debug( "Run Thread Done :" + Thread.currentThread() );
            return true;
        }

    }

}
